package com.alameda.service.impl.menu;

import com.alameda.common.base.constant.DataPermTypeConstant;
import com.alameda.common.utils.*;
import com.alameda.mapper.menu.AdMenuMapper;
import com.alameda.menu.bo.AdMenuBO;
import com.alameda.menu.bo.AdMenuExportBO;
import com.alameda.menu.dto.AdMenuDTO;
import com.alameda.menu.vo.AdMenuVO;
import com.alameda.pojo.menu.AdMenuDO;
import com.alameda.pojo.user.AdUserDO;
import com.alameda.service.menu.AdMenuService;
import com.alameda.service.user.AdUserService;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 菜单 业务逻辑层 具体实现
 *
 * @author Lv
 * @date 2024/06/23
 */
@Slf4j
@Service
public class AdMenuServiceImpl extends ServiceImpl<AdMenuMapper, AdMenuDO> implements AdMenuService {

    @Autowired
    private AdMenuMapper adMenuMapper;

    @Autowired
    private AdUserService adUserService;

    /**
     * 读取菜单树
     *
     * @return
     */
    @Override
    public List<AdMenuVO> loadMenu() {
        Long userId = ThreadLocalUtils.getUserId();
        AdUserDO adUserDO = adUserService.getById(userId);

        List<AdMenuDO> adMenuDOList = null;
        Integer power = adUserDO.getPower();

        if (DataPermTypeConstant.DATA_TYPE_USER.equals(String.valueOf(power))) {
            LambdaQueryWrapper<AdMenuDO> queryWrapper = Wrappers.lambdaQuery(AdMenuDO.class).orderByAsc(AdMenuDO::getMenuOrderNumber);
            adMenuDOList = this.list(queryWrapper);
        } else {
            adMenuDOList = this.lambdaQuery()
                    .in(AdMenuDO::getMenuPower, power)
                    .orderByAsc(AdMenuDO::getMenuOrderNumber)
                    .list();
        }

        log.info("菜单数据menuDOList :{}", JSONObject.toJSONString(adMenuDOList));
        return convertToTreeMenuList(adMenuDOList);
    }

    /**
     * 读取菜单信息列表
     *
     * @param adMenuDTO 菜单信息
     * @return
     */
    @Override
    public IPage<AdMenuVO> listMenuInfo(AdMenuDTO adMenuDTO) {
        IPage<AdMenuVO> page = new Page<>(adMenuDTO.getPageNum(), adMenuDTO.getPageSize());
        return adMenuMapper.findByMenuNameLike(page, adMenuDTO.getMenuName());
    }

    /**
     * 导出excel
     *
     * @param adMenuDTO 菜单信息
     */
    @Override
    public void export(AdMenuDTO adMenuDTO) {
        List<AdMenuExportBO> adMenuExportBOList = getExportData(adMenuDTO);
        ExcelUtils.export(adMenuExportBOList, AdMenuExportBO.class, "菜单列表");
    }

    /**
     * 导出菜单json
     */
    @Override
    public void exportMenu() {
        List<AdMenuVO> adMenuVOList = loadMenu();
        List<AdMenuBO> adMenuBOList = new ArrayList<>();

        adMenuVOList.forEach(item -> {
            List<AdMenuVO> childrenList = item.getChildren();
            List<AdMenuBO.Children> menuChildrenList = new ArrayList<>();

            childrenList.forEach(itemChildren -> {
                AdMenuBO.Meta meta = AdMenuBO.Meta.builder().build();
                meta.setTitle(itemChildren.getMenuName());
                meta.setIcon(itemChildren.getMenuIcon());
                meta.setNoCache(Boolean.TRUE.toString());

                AdMenuBO.Children children = AdMenuBO.Children.builder().build();
                children.setPath(itemChildren.getMenuPath());
                children.setComponent(itemChildren.getMenuComponent());
                children.setName(PubUtils.convertTransverseToUpCamelCase(itemChildren.getMenuPath()));
                children.setMeta(meta);

                menuChildrenList.add(children);
            });

            AdMenuBO.Meta meta = AdMenuBO.Meta.builder().build();
            meta.setTitle(item.getMenuName());
            meta.setIcon(item.getMenuIcon());
            meta.setNoCache(Boolean.TRUE.toString());

            AdMenuBO adMenuBO = AdMenuBO.builder().build();
            adMenuBO.setPath(item.getMenuPath());
            adMenuBO.setName(PubUtils.convertTransverseToUpCamelCase(item.getMenuPath()));
            adMenuBO.setComponent(Optional.ofNullable(item.getMenuComponent()).orElse("Layout"));
            adMenuBO.setRedirect(item.getMenuPath());
            adMenuBO.setAlwaysShow(Boolean.TRUE);
            adMenuBO.setHidden(Boolean.TRUE);
            adMenuBO.setMeta(meta);
            adMenuBO.setChildren(menuChildrenList);
            adMenuBO.setMeta(meta);

            adMenuBOList.add(adMenuBO);
        });

        log.info("导出菜单信息:{}", JSONObject.toJSONString(adMenuBOList));

        TextUtils.downloadText(JSONObject.toJSONString(adMenuBOList), "菜单信息", HttpUtils.getHttpServletResponse());
    }

    /**
     * 删除菜单
     *
     * @param idList 菜单id集合
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean removeByIdList(List<Long> idList) {
        List<AdMenuDO> adMenuDOList = this.findByIdInOrderByCreateTimeDesc(idList);

        List<Long> menuParentIdList = new ArrayList<>();
        adMenuDOList.forEach(item -> {
            Long menuParentId = item.getMenuParentId();
            if (PubUtils.isNull(menuParentId)) {
                menuParentIdList.add(menuParentId);
            }
        });

        List<AdMenuDO> childMenuList = findChildList(menuParentIdList);

        List<Long> childMenuIdList = childMenuList.stream().map(AdMenuDO::getId)
                .filter(item -> !idList.contains(item))
                .collect(Collectors.toList());

        this.removeByIds(childMenuIdList);
        return this.removeByIds(idList);
    }

    /**
     * 获取子菜单信息
     *
     * @param menuParentIdList 菜单父类id
     * @return
     */
    private List<AdMenuDO> findChildList(List<Long> menuParentIdList) {
        return this.lambdaQuery().in(AdMenuDO::getMenuParentId, menuParentIdList).list();
    }

    /**
     * 获取导出数据
     *
     * @param adMenuDTO 菜单信息
     * @return
     */
    private List<AdMenuExportBO> getExportData(AdMenuDTO adMenuDTO) {
        if (PubUtils.isEmpty(adMenuDTO.getIdList())) {
            IPage<AdMenuVO> pageData = listMenuInfo(adMenuDTO);
            return ObjectUtils.convertList(pageData.getRecords(), AdMenuExportBO.class);
        } else {
            List<AdMenuDO> adMenuDOList = this.findByIdInOrderByCreateTimeDesc(adMenuDTO.getIdList());
            return ObjectUtils.convertList(adMenuDOList, AdMenuExportBO.class);
        }
    }

    /**
     * 数据模型转换
     *
     * @param adMenuDOList 菜单列表
     * @return
     */
    private List<AdMenuVO> convertToTreeMenuList(List<AdMenuDO> adMenuDOList) {
        List<AdMenuVO> adMenuVOList = new ArrayList<>();

        for (AdMenuDO adMenuDO : adMenuDOList) {
            AdMenuVO adMenuVO = convertToMenuVO(adMenuDO);
            if (PubUtils.isNull(adMenuVO.getMenuParentId())) {
                recursionFn(adMenuVO, adMenuDOList);
                adMenuVOList.add(adMenuVO);
            }
        }

        adMenuVOList.sort(Comparator.comparing(AdMenuVO::getMenuOrderNumber));
        log.info("menuVOList :{}", JSONObject.toJSONString(adMenuVOList));
        return adMenuVOList;
    }

    /**
     * 数据模型转换
     *
     * @param adMenuDO 菜单对象
     * @return
     */
    private AdMenuVO convertToMenuVO(AdMenuDO adMenuDO) {
        return ObjectUtils.convert(adMenuDO, AdMenuVO.class);
    }

    /**
     * 将菜单转换为树形结构(递归算法)
     *
     * @param adMenuVO     菜单视图对象
     * @param adMenuDOList 菜单数据集合
     */
    private void recursionFn(AdMenuVO adMenuVO, List<AdMenuDO> adMenuDOList) {
        List<AdMenuVO> childList = getChildList(adMenuVO, adMenuDOList);
        adMenuVO.setChildren(childList);
        for (AdMenuVO vo : childList) {
            if (hasChild(vo, adMenuDOList)) {
                recursionFn(vo, adMenuDOList);
            }
        }
    }

    /**
     * 是否存在子菜单
     *
     * @param adMenuVO     菜单视图对象
     * @param adMenuDOList 菜单数据集合
     * @return
     */
    private boolean hasChild(AdMenuVO adMenuVO, List<AdMenuDO> adMenuDOList) {
        return getChildList(adMenuVO, adMenuDOList).size() > 0;
    }

    /**
     * 获取子类菜单
     *
     * @param adMenuVO     菜单视图对象
     * @param adMenuDOList 菜单数据集合
     * @return
     */
    private List<AdMenuVO> getChildList(AdMenuVO adMenuVO, List<AdMenuDO> adMenuDOList) {
        List<AdMenuVO> adMenuVOList = new ArrayList<>();

        for (AdMenuDO adMenuDO : adMenuDOList) {
            if (PubUtils.isNull(adMenuDO.getMenuParentId())) {
                continue;
            }

            if (adMenuDO.getMenuParentId().equals(adMenuVO.getId())) {
                AdMenuVO convert = convertToMenuVO(adMenuDO);
                adMenuVOList.add(convert);
            }
        }

        return adMenuVOList;
    }
}
